package dbMysql

import (
	"context"
	"database/sql"
	"encoding/json"
	"fmt"
	"io"
	"strings"
	"sync"
	"time"

	"gitee.com/kingzyt/common/log"
	. "gitee.com/kingzyt/common/util"

	"github.com/go-sql-driver/mysql"
)

const (
	sql_query_execute_deadline_t = time.Second * 10
)

var g_characterSet = "utf8mb4"

func SetCharacterSet(set string) {
	g_characterSet = set
}

func GetSqlDB(driver string, userName string, password string, mysqlDBAddr string, dbName string, maxOpenConns int, maxIdleConns int) (sqlDB *sql.DB, err error) {
	sqlDB, err = sql.Open(driver, fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=%s&loc=Asia%%2FShanghai&parseTime=true", userName, password, mysqlDBAddr, dbName, g_characterSet))
	if err != nil {
		return
	}
	sqlDB.SetMaxOpenConns(maxOpenConns)
	sqlDB.SetMaxIdleConns(maxIdleConns)
	/*
		你是否经常会在你的log文件中看到下面的exception？
		Communications link failure due to underlying exception:

		** BEGIN NESTED EXCEPTION **

		java.net.SocketException
		MESSAGE: Broken pipe

		STACKTRACE:

		java.net.SocketException: Broken pipe
		       at java.net.SocketOutputStream.socketWrite0(Native Method)
		       at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:92)
		       at java.net.SocketOutputStream.write(SocketOutputStream.java:136)
		       at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:65)


		从这个exception中我们可以得出是关于Socket的问题， 那么肯定就是关于网络方面的原因， 比如mysql down了。 或者网络有问题连不上mysql。

		mysql会自动关闭掉空闲时间大于wait_timeout的连接。wait_timeout在可以在mysql配置文件里配置，默认的值是28800也就是8个小时。
		也就是说如果一个connection在8个小时候之内没有用使用过过的话， mysql server会把这个connection销毁掉。
		这就是发生这个问题的原因了。 那么如何解决呢？
		我们一般都用连接池来保存连接， 一般连接池都会有许多参数让你的连接池更高效。
		我们以c3p0作为例子。 http://www.mchange.com/projects/c3p0/index.html， 这个是c3p0的文档，
		里面有许多参数，但是我们只要注意这个参数就行了。 maxIdleTime以下是对这个参数的解释。

		引用
		maxIdleTime
		    Default: 0
		    Seconds a Connection can remain pooled but unused before being discarded. Zero means idle connections never expire. [See "Basic Pool Configuration"]

		在c3p0中maxIdleTime的默认值是0,也就是说永远都不会被expire， 所以解决这个问题的办法就是设置maxIdleTime比mysql的wait_timeout的值小一些就行了。
	*/
	sqlDB.SetConnMaxLifetime(time.Hour * 5)
	err = sqlDB.Ping()
	if err != nil {
		sqlDB.Close()
		sqlDB = nil
		return
	}

	return
}

type SimpleMySqlRWer struct {
	name               string
	dbName             string
	sqlDBWriter        *sql.DB
	newSqlDBWriterFunc func() *sql.DB
	sqlDBWriterMutex   sync.Mutex

	sqlDBReader        *sql.DB
	newSqlDBReaderFunc func() *sql.DB
	sqlDBReaderMutex   sync.Mutex

	rTxOptions  *sql.TxOptions
	rwTxOptions *sql.TxOptions
}

func NewSimpleMySqlRWer(userName string, password string, mysqlDBAddr string, dbName string) *SimpleMySqlRWer {
	addr := MergeSameMachineServerIPToLocal(mysqlDBAddr)

	name := fmt.Sprintf("SqlRWer_%s_%s", dbName, mysqlDBAddr)

	log.Info(name, log.I, "mysql db:%s, connect ok", dbName)

	rlt := &SimpleMySqlRWer{
		name:        name,
		dbName:      dbName,
		rTxOptions:  &sql.TxOptions{Isolation: sql.LevelDefault, ReadOnly: true},
		rwTxOptions: &sql.TxOptions{Isolation: sql.LevelDefault, ReadOnly: false},
	}

	rlt.newSqlDBWriterFunc = func() *sql.DB {
		log.Info(rlt.name, log.I, "sql writer link reset")
		if rlt.sqlDBWriter != nil {
			rlt.sqlDBWriter.Close()
			rlt.sqlDBWriter = nil
		}

		sqlDBWriter, err := GetSqlDB("mysql", userName, password, addr, dbName, 4, 2)
		if err != nil {
			log.Error(rlt.name, "mysql db(writer) connect err: %s", err)
			return nil
		}
		_, err = sqlDBWriter.Exec("set names " + g_characterSet + ";")
		if err != nil {
			log.Error(rlt.name, "mysql db(writer) do 'set names %s' err: %s", g_characterSet, err)
			return nil
		}

		_, err = sqlDBWriter.Exec("set @@autocommit = 0;")
		if err != nil {
			log.Error(rlt.name, "mysql db(writer) do 'set autocommit = 0' err: %s", err)
			return nil
		}

		rlt.sqlDBWriter = sqlDBWriter
		return sqlDBWriter
	}

	rlt.newSqlDBReaderFunc = func() *sql.DB {
		log.Info(rlt.name, log.I, "sql reader link reset")
		if rlt.sqlDBReader != nil {
			rlt.sqlDBReader.Close()
			rlt.sqlDBReader = nil
		}

		sqlDBReader, err := GetSqlDB("mysql", userName, password, addr, dbName, 4, 2)
		if err != nil {
			log.Error(rlt.name, "mysql db(reader) connect err: %s", err)
			return nil
		}
		_, err = sqlDBReader.Exec("set names " + g_characterSet + ";")
		if err != nil {
			log.Error(rlt.name, "mysql db(reader) do 'set names %s' err: %s", g_characterSet, err)
			return nil
		}

		//read如果不是autocommit的话，就会获取最近的一次快照，很可能不是刚刚修改的结果；如果这里不是autocommit，那后面read的时候就要加事务来处理
		/*_, err = sqlDBReader.Exec("set @@autocommit = 0;")
		if err != nil {
			log.Error(rlt.name, "mysql db(reader) do 'set autocommit = 0' err: %s", err)
			return nil
		}*/

		rlt.sqlDBReader = sqlDBReader

		return sqlDBReader
	}

	return rlt
}

func (self *SimpleMySqlRWer) GetDBName() string {
	return self.dbName
}

func (self *SimpleMySqlRWer) Start() bool {
	if self.newSqlDBWriterFunc() == nil {
		return false
	}
	if self.newSqlDBReaderFunc() == nil {
		return false
	}
	return true
}

func (self *SimpleMySqlRWer) Quit() {
	func() {
		self.sqlDBWriterMutex.Lock()
		defer self.sqlDBWriterMutex.Unlock()
		if self.sqlDBWriter != nil {
			self.sqlDBWriter.Close()
		}
	}()

	func() {
		self.sqlDBReaderMutex.Lock()
		defer self.sqlDBReaderMutex.Unlock()
		if self.sqlDBReader != nil {
			self.sqlDBReader.Close()
		}
	}()
}

func (self *SimpleMySqlRWer) resetSqlDbLink(err error) {
	if err != nil && (strings.Contains(err.Error(), "database is closed") ||
		strings.Contains(err.Error(), "broken pipe") ||
		strings.Contains(err.Error(), "connection refused") ||
		strings.Contains(err.Error(), "bad connection")) {
		func() {
			self.sqlDBWriterMutex.Lock()
			defer self.sqlDBWriterMutex.Unlock()
			self.newSqlDBWriterFunc()
		}()
		func() {
			self.sqlDBReaderMutex.Lock()
			defer self.sqlDBReaderMutex.Unlock()
			self.newSqlDBReaderFunc()
		}()
	}
}
func (self *SimpleMySqlRWer) getSqlWriter() *sql.DB {
	self.sqlDBWriterMutex.Lock()
	defer self.sqlDBWriterMutex.Unlock()

	if self.sqlDBWriter == nil {
		self.newSqlDBWriterFunc()
	}
	return self.sqlDBWriter
}
func (self *SimpleMySqlRWer) getSqlReader() *sql.DB {
	self.sqlDBReaderMutex.Lock()
	defer self.sqlDBReaderMutex.Unlock()

	if self.sqlDBReader == nil {
		self.newSqlDBReaderFunc()
	}
	return self.sqlDBReader
}

func GetMySqlDateTime(t int64) string {
	return TimestampToTime(t).Format(time.RFC3339)[:19]
}
func GetMySqlDateTimeByTime(t time.Time) string {
	return t.Format(time.RFC3339)[:19]
}

func getContextDefault() context.Context {
	return getContextWithDeadline(sql_query_execute_deadline_t)
}

func getContextWithDeadline(t time.Duration) context.Context {
	c, _ := context.WithDeadline(context.Background(), time.Now().Add(t))
	return c
}

type ScanFaceType int8

const (
	SCAN_FACE_TYPE_VALUE ScanFaceType = iota
	SCAN_FACE_TYPE_JSON_INT32LIST
	SCAN_FACE_TYPE_JSON_INT64LIST
	SCAN_FACE_TYPE_JSON_STRLIST
)

func isUseJsonList(rowScanFace []interface{}) bool {
	useJsonList := false
	for _, face := range rowScanFace {
		_, ok := face.(*[]int32)
		if ok {
			useJsonList = true
			break
		}

		_, ok = face.(*[]int64)
		if ok {
			useJsonList = true
			break
		}

		_, ok = face.(*[]string)
		if ok {
			useJsonList = true
			break
		}
	}
	return useJsonList
}
func wrapArgs(args []interface{}) (rlt []interface{}, err error) {
	proc := func(arg interface{}) (err error) {
		buf, err := json.Marshal(arg)
		if err != nil {
			return err
		}
		rlt = append(rlt, buf)
		return
	}

	for _, arg := range args {
		_, ok := arg.([]int32)
		if ok {
			err = proc(arg)
			if err != nil {
				return
			}
			continue
		}

		_, ok = arg.([]int64)
		if ok {
			err = proc(arg)
			if err != nil {
				return
			}
			continue
		}

		_, ok = arg.([]string)
		if ok {
			err = proc(arg)
			if err != nil {
				return
			}
			continue
		}

		rlt = append(rlt, arg)
	}
	return
}

func encodeWithStandIn(rowScanFace []interface{}, args []interface{}) (useJsonList bool, standins []interface{}, standinTypes []ScanFaceType, wrappedArgs []interface{}, err error) {
	wrappedArgs, err = wrapArgs(args)
	if err != nil {
		return
	}

	useJsonList = isUseJsonList(rowScanFace)
	if !useJsonList {
		return false, rowScanFace, nil, wrappedArgs, nil
	}

	for _, face := range rowScanFace {
		_, ok := face.(*[]int32)
		if ok {
			standins = append(standins, &[]byte{})
			standinTypes = append(standinTypes, SCAN_FACE_TYPE_JSON_INT32LIST)
			continue
		}

		_, ok = face.(*[]int64)
		if ok {
			standins = append(standins, &[]byte{})
			standinTypes = append(standinTypes, SCAN_FACE_TYPE_JSON_INT64LIST)
			continue
		}

		_, ok = face.(*[]string)
		if ok {
			standins = append(standins, &[]byte{})
			standinTypes = append(standinTypes, SCAN_FACE_TYPE_JSON_STRLIST)
			continue
		}

		standins = append(standins, face)
		standinTypes = append(standinTypes, SCAN_FACE_TYPE_VALUE)
	}
	return
}
func decodeWithStandIn(rowScanFace []interface{}, standinFaces []interface{}, standinTypes []ScanFaceType) (err error) {
	for i, standtype := range standinTypes {
		switch standtype {
		case SCAN_FACE_TYPE_JSON_INT32LIST, SCAN_FACE_TYPE_JSON_INT64LIST, SCAN_FACE_TYPE_JSON_STRLIST:
			err = json.Unmarshal(*(standinFaces[i]).(*[]byte), rowScanFace[i])
			if err != nil {
				return
			}
		}
	}
	return
}

func (self *SimpleMySqlRWer) ReadQueryRow(query string, rowScanFace []interface{}, onRlt func(rowScanFace []interface{}), args ...interface{}) (err error) {
	defer func() {
		if err != nil {
			log.Error(self.GetDBName(), "err sql str: %s, args:%v", query, args)
		}

		self.resetSqlDbLink(err)
	}()

	reader := self.getSqlReader()
	if reader == nil {
		return fmt.Errorf("db link err")
	}

	useJsonList, standins, standinTypes, wrappedArgs, err := encodeWithStandIn(rowScanFace, args)
	if err != nil {
		return
	}

	row := reader.QueryRowContext(getContextDefault(), query, wrappedArgs...)
	err = row.Scan(standins...)
	if err != nil {
		if err == sql.ErrNoRows {
			err = nil
		}
	} else {
		if useJsonList {
			err = decodeWithStandIn(rowScanFace, standins, standinTypes)
			if err != nil {
				return
			}
		}

		if onRlt != nil {
			onRlt(rowScanFace)
		}
	}

	return
}

func (self *SimpleMySqlRWer) ReadQueryRows(query string, rowScanFace []interface{}, onOneRow func(rowScanFace []interface{}), args ...interface{}) (err error) {
	if onOneRow == nil {
		panic(fmt.Errorf("onOneRow is nil"))
	}

	defer func() {
		if err != nil {
			log.Error(self.GetDBName(), "err sql str: %s, args:%v", query, args)
		}

		self.resetSqlDbLink(err)
	}()

	reader := self.getSqlReader()
	if reader == nil {
		return fmt.Errorf("db link err")
	}

	useJsonList, standins, standinTypes, wrappedArgs, err := encodeWithStandIn(rowScanFace, args)
	if err != nil {
		return
	}

	var rows *sql.Rows
	rows, err = reader.QueryContext(getContextDefault(), query, wrappedArgs...)
	if err != nil {
		return
	}
	defer rows.Close() // multiple defer will do at end

	// head check, make sure that rows.Next() is true then more row can read
	for rows.Next() {
		err = rows.Scan(standins...)
		if err != nil {
			return
		}

		if useJsonList {
			err = decodeWithStandIn(rowScanFace, standins, standinTypes)
			if err != nil {
				return
			}
		}
		onOneRow(rowScanFace)
	}
	err = rows.Err()
	if err != nil {
		return
	}

	return
}

func (self *SimpleMySqlRWer) WriteQuery(query string, args ...interface{}) (rowsAffected int64, err error) {
	defer func() {
		if err != nil {
			log.Error(self.GetDBName(), "err sql str: %s, args:%v", query, args)
		}

		self.resetSqlDbLink(err)
	}()

	writer := self.getSqlWriter()
	if writer == nil {
		return 0, fmt.Errorf("db link err")
	}

	tx, err := writer.BeginTx(getContextDefault(), self.rwTxOptions)
	if err != nil {
		return 0, err
	}
	defer func() {
		if err == nil {
			err = tx.Commit()
			if err == sql.ErrTxDone {
				err = ErrTxTimeout
			}
		} else {
			// err is not null, if we use Rollback's result to overwrite this err, normally Rollback is no err, so 'err' will be set to nil, that's wrong
			tx.Rollback()
		}
	}()

	wrappedArgs, err := wrapArgs(args)
	if err != nil {
		return
	}

	rlt, err := tx.Exec(query, wrappedArgs...)
	if err != nil {
		return 0, err
	}

	if rlt != nil {
		rowsAffected, _ = rlt.RowsAffected()
	}
	return
}

type SqlQuery struct {
	Query string
	Args  []interface{}
}

func (self *SimpleMySqlRWer) WriteQueries(sqls []*SqlQuery) (err error) {
	defer func() {
		self.resetSqlDbLink(err)
	}()

	writer := self.getSqlWriter()
	if writer == nil {
		return fmt.Errorf("db link err")
	}

	tx, err := writer.BeginTx(getContextDefault(), self.rwTxOptions)
	if err != nil {
		return err
	}

	defer func() {
		if err == nil {
			err = tx.Commit()
			if err == sql.ErrTxDone {
				err = ErrTxTimeout
			}
		} else {
			tx.Rollback()
		}
	}()

	for i := 0; i < len(sqls); i++ {
		query := sqls[i]
		if query.Query == "" {
			continue
		}
		_, err = tx.Exec(query.Query, query.Args...)
		if err != nil {
			log.Error(self.GetDBName(), "err sql str: %s, args:%v", query.Query, query.Args)
			return err
		}
	}

	return
}
func (self *SimpleMySqlRWer) WriteQueriesWithDeadline(t time.Duration, sqls []*SqlQuery) (err error) {
	return self.TransactionWithDeadline(t, func(sqlrw *SimpleMySqlRWer, tx *sql.Tx) {
		for _, sql_ := range sqls {
			sqlrw.TxWriteQuery(tx, sql_.Query, sql_.Args...)
		}
	})
}

func (self *SimpleMySqlRWer) ReadWriteQueryRow(query string, rowScanFace []interface{}, onRlt func(rowScanFace []interface{}), args ...interface{}) (err error) {
	defer func() {
		if err != nil {
			log.Error(self.GetDBName(), "err sql str: %s, args:%v", query, args)
		}

		self.resetSqlDbLink(err)
	}()

	writer := self.getSqlWriter()
	if writer == nil {
		return fmt.Errorf("db link err")
	}

	tx, err := writer.BeginTx(getContextDefault(), self.rwTxOptions)
	if err != nil {
		return err
	}

	defer func() {
		if err == nil {
			err = tx.Commit()
			if err == sql.ErrTxDone {
				err = ErrTxTimeout
			}
		} else {
			tx.Rollback()
		}
	}()

	row := tx.QueryRow(query, args...)
	err = row.Scan(rowScanFace...)
	if err != nil {
		if err == sql.ErrNoRows {
			err = nil
		}
	} else {
		if onRlt != nil {
			onRlt(rowScanFace)
		}
	}

	return
}

func (self *SimpleMySqlRWer) Transaction(procInTx func(sqlrw *SimpleMySqlRWer, tx *sql.Tx)) (err error) {
	return self.TransactionWithContext(getContextDefault(), procInTx)
}
func (self *SimpleMySqlRWer) TransactionWithDeadline(t time.Duration, procInTx func(sqlrw *SimpleMySqlRWer, tx *sql.Tx)) (err error) {
	return self.TransactionWithContext(getContextWithDeadline(t), procInTx)
}

var ErrTxTimeout = fmt.Errorf("transaction is timeout")

func (self *SimpleMySqlRWer) TransactionWithContext(con context.Context, procInTx func(sqlrw *SimpleMySqlRWer, tx *sql.Tx)) (err error) {
	defer func() {
		self.resetSqlDbLink(err)
	}()

	writer := self.getSqlWriter()
	if writer == nil {
		return fmt.Errorf("db link err")
	}

	tx, err := writer.BeginTx(con, self.rwTxOptions)
	if err != nil {
		return err
	}

	defer func() {
		if err == nil {
			err = tx.Commit()
			if err == sql.ErrTxDone {
				err = ErrTxTimeout
			}
		} else {
			tx.Rollback()
		}
	}()

	defer func() {
		if r := recover(); r != nil {
			er, erOk := r.(error)
			// 有deadline的情况下，超时会在底层自动发送rollback到mysql，造成这里出现ErrTxDone
			if erOk && er == sql.ErrTxDone {
				err = ErrTxTimeout
			} else {
				err = fmt.Errorf("transaction err: %v", r)
				PrintPanicTrace()
			}
		}
	}()
	procInTx(self, tx)

	return
}

func (self *SimpleMySqlRWer) TxReadQueryRow(tx *sql.Tx, query string, rowScanFace []interface{}, onRlt func(rowScanFace []interface{}), args ...interface{}) {
	var err error
	defer func() {
		if err != nil {
			log.Error(self.GetDBName(), "err sql str: %s, args:%v", query, args)
			panic(err)
		}
	}()

	useJsonList, standins, standinTypes, wrappedArgs, err := encodeWithStandIn(rowScanFace, args)
	if err != nil {
		return
	}

	row := tx.QueryRow(query, wrappedArgs...)

	err = row.Scan(standins...)
	if err != nil {
		if err == sql.ErrNoRows {
			err = nil
		}
	} else {
		if useJsonList {
			err = decodeWithStandIn(rowScanFace, standins, standinTypes)
			if err != nil {
				return
			}
		}

		if onRlt != nil {
			onRlt(rowScanFace)
		}
	}
	return
}
func (self *SimpleMySqlRWer) TxReadQueryRows(tx *sql.Tx, query string, rowScanFace []interface{}, onOneRow func(rowScanFace []interface{}), args ...interface{}) {
	var err error
	defer func() {
		if err != nil {
			log.Error(self.GetDBName(), "err sql str: %s, args:%v", query, args)
			panic(err)
		}
	}()

	useJsonList, standins, standinTypes, wrappedArgs, err := encodeWithStandIn(rowScanFace, args)
	if err != nil {
		return
	}

	rows, err := tx.Query(query, wrappedArgs...)
	if err != nil {
		return
	}
	defer rows.Close()

	// head check, make sure that rows.Next() is true then more row can read
	for rows.Next() {
		err = rows.Scan(standins...)
		if err != nil {
			return
		}

		if useJsonList {
			err = decodeWithStandIn(rowScanFace, standins, standinTypes)
			if err != nil {
				return
			}
		}
		onOneRow(rowScanFace)
	}
	err = rows.Err()
	if err != nil {
		return
	}

	return
}
func (self *SimpleMySqlRWer) TxWriteQuery(tx *sql.Tx, query string, args ...interface{}) (rowsAffected int64) {
	var err error
	defer func() {
		if err != nil {
			log.Error(self.GetDBName(), "err sql str: %s, args:%v", query, args)
			panic(err)
		}
	}()

	wrappedArgs, err := wrapArgs(args)
	if err != nil {
		return
	}

	rlt, err := tx.Exec(query, wrappedArgs...)
	if err != nil {
		return 0
	}

	if rlt != nil {
		rowsAffected, _ = rlt.RowsAffected()
	}
	return
}

func (self *SimpleMySqlRWer) LoadDataLocalInFile(tableName string, loadFieldsMark string, dataReader func() io.Reader) error {
	dataName := tableName + "_load_local_data"
	mysql.RegisterReaderHandler(dataName, dataReader)
	_, err := self.WriteQuery(`Load Data local InFile 'Reader::` + dataName + `' Into Table ` + tableName + ` character set utf8 Fields Terminated By ',' Lines Terminated By ';' (` + loadFieldsMark + `); `)
	mysql.DeregisterReaderHandler(dataName)
	return err
}

func reserveBuffer(buf []byte, size int) []byte {
	if cap(buf) < size {
		// Grow buffer exponentially
		newBuf := make([]byte, len(buf)*2+size)
		copy(newBuf, buf)
		buf = newBuf
	}
	return buf[:size]
}

func SqlEscapeBytesBackslash(buf, v []byte) []byte {
	pos := 0
	buf = reserveBuffer(buf, len(v)*2)

	for _, c := range v {
		switch c {
		case '\x00':
			buf[pos] = '\\'
			buf[pos+1] = '0'
			pos += 2
		case '\n':
			buf[pos] = '\\'
			buf[pos+1] = 'n'
			pos += 2
		case '\r':
			buf[pos] = '\\'
			buf[pos+1] = 'r'
			pos += 2
		case '\x1a':
			buf[pos] = '\\'
			buf[pos+1] = 'Z'
			pos += 2
		case '\'':
			buf[pos] = '\\'
			buf[pos+1] = '\''
			pos += 2
		case '"':
			buf[pos] = '\\'
			buf[pos+1] = '"'
			pos += 2
		case '\\':
			buf[pos] = '\\'
			buf[pos+1] = '\\'
			pos += 2
		default:
			buf[pos] = c
			pos++
		}
	}

	return buf[:pos]
}

func SqlEscapeStringBackslash(buf []byte, v string) []byte {
	pos := 0
	buf = reserveBuffer(buf, len(v)*2)

	for i := 0; i < len(v); i++ {
		c := v[i]
		switch c {
		case '\x00':
			buf[pos] = '\\'
			buf[pos+1] = '0'
			pos += 2
		case '\n':
			buf[pos] = '\\'
			buf[pos+1] = 'n'
			pos += 2
		case '\r':
			buf[pos] = '\\'
			buf[pos+1] = 'r'
			pos += 2
		case '\x1a':
			buf[pos] = '\\'
			buf[pos+1] = 'Z'
			pos += 2
		case '\'':
			buf[pos] = '\\'
			buf[pos+1] = '\''
			pos += 2
		case '"':
			buf[pos] = '\\'
			buf[pos+1] = '"'
			pos += 2
		case '\\':
			buf[pos] = '\\'
			buf[pos+1] = '\\'
			pos += 2
		default:
			buf[pos] = c
			pos++
		}
	}

	return buf[:pos]
}
